import fs from "fs/promises";
import path from "path";

/**
 * Recursively iterate over all files in a directory and its subdirectories.
 * returns a list of all file paths. (absolute path)
 */
async function forEachFile(
  dirPath: string,
  cb?: (path: string) => any,
): Promise<string[]> {
  const files = await fs.readdir(dirPath);
  const result: string[] = [];
  for (const file of files) {
    const filePath = path.join(dirPath, file);
    const stat = await fs.stat(filePath);
    if (stat.isDirectory()) {
      const subFiles = await forEachFile(filePath);
      result.push(...subFiles);
    } else {
      result.push(filePath);
    }
    if (cb) {
      cb(filePath);
    }
  }
  return result;
}

async function getFileSize(filePath: string): Promise<number> {
  return (await fs.stat(filePath)).size;
}

async function copyFile(src: string, dest: string): Promise<void> {
  return await fs.copyFile(src, dest);
}

async function readFile(filePath: string): Promise<string> {
  return await fs.readFile(filePath, "utf-8");
}

async function readRawFile(filePath: string) {
  return await fs.readFile(filePath);
}

async function createFile(filePath: string): Promise<void> {
  return await fs.writeFile(filePath, "");
}

async function isFileExists(filePath: string): Promise<boolean> {
  try {
    await fs.access(filePath);
    return true;
  } catch (e) {
    return false;
  }
}

async function writeFile(filePath: string, data: string): Promise<void> {
  return await fs.writeFile(filePath, data);
}

async function createDirAndWriteFile(
  filePath: string,
  data: string,
): Promise<void> {
  await fs.mkdir(path.dirname(filePath), { recursive: true });
  return await writeFile(filePath, data);
}

async function createDirIfNotExists(dirPath: string): Promise<void> {
  try {
    await fs.access(dirPath);
  } catch (e) {
    await fs.mkdir(dirPath, { recursive: true });
  }
}

async function updateFile(
  filePath: string,
  cb: (data: string) => string | Promise<string>,
): Promise<string> {
  const data = await readFile(filePath);
  const newData = await cb(data);
  await writeFile(filePath, newData);
  return newData;
}

async function isFolderEmpty(
  dirPath: string,
  except?: (RegExp | string)[],
): Promise<boolean> {
  const files = await fs.readdir(dirPath);
  for (const file of files) {
    if (
      except &&
      except.some((e) => {
        if (e instanceof RegExp) {
          return e.test(file);
        } else {
          return e === file;
        }
      })
    ) {
      continue;
    }
    return false;
  }
  return true;
}

function fileName(filePath: string): string {
  return path.basename(filePath);
}

async function deleteFolder(path: string): Promise<void> {
  return await fs.rm(path, { recursive: true, force: true });
}

function cleanFolder(
  dirPath: string,
  except?: (RegExp | string)[],
): Promise<void> {
  return new Promise((resolve, reject) => {
    fs.readdir(dirPath)
      .then((files) => {
        const promises = files.map((file) => {
          if (
            except &&
            except.some((e) => {
              if (e instanceof RegExp) {
                return e.test(file);
              } else {
                return e === file;
              }
            })
          ) {
            return Promise.resolve();
          }
          return fs.rm(path.join(dirPath, file), { recursive: true });
        });
        Promise.all(promises)
          .then(() => resolve())
          .catch((e) => reject(e));
      })
      .catch((e) => reject(e));
  });
}

const ReadMode = {
  READ: 0x1,
  CREATE: 0x2,
  CREATE_DIR: 0x4,
};

async function read(
  file: string,
  config: {
    mode: (typeof ReadMode)[keyof typeof ReadMode];
    raw?: boolean;
  },
): Promise<string | Buffer | null> {
  if (!file) {
    return null;
  }
  const { mode } = config;

  // 如果模式包含 CREATE，处理文件创建逻辑
  if (mode & ReadMode.CREATE) {
    // 如果还需要创建目录，先创建目录
    if (mode & ReadMode.CREATE_DIR) {
      await createDirIfNotExists(path.dirname(file));
    }
    // 如果文件不存在，创建文件
    if (!(await isFileExists(file))) {
      await createFile(file);
    }
  }

  // 如果模式包含 READ，直接读取文件
  if (mode & ReadMode.READ) {
    return await (config.raw ? readRawFile(file) : readFile(file));
  }

  // 如果既不是 READ 也不是 CREATE 模式，返回null
  return null;
}

/**
 * 检测两个文件夹路径是否有重叠
 * @param folder1 第一个文件夹路径
 * @param folder2 第二个文件夹路径
 * @returns 如果有重叠返回 true，否则返回 false
 */
function areFoldersOverlapping(folder1: string, folder2: string): boolean {
  // 标准化路径
  const normalizedFolder1 = path.resolve(folder1);
  const normalizedFolder2 = path.resolve(folder2);

  // 检查 folder1 是否包含 folder2
  const isFolder1ContainsFolder2 = normalizedFolder2.startsWith(
    normalizedFolder1 + path.sep,
  );
  // 检查 folder2 是否包含 folder1
  const isFolder2ContainsFolder1 = normalizedFolder1.startsWith(
    normalizedFolder2 + path.sep,
  );

  return isFolder1ContainsFolder2 || isFolder2ContainsFolder1;
}

export {
  getFileSize,
  readFile,
  forEachFile,
  copyFile,
  writeFile,
  updateFile,
  createFile,
  fileName,
  createDirAndWriteFile,
  createDirIfNotExists,
  isFileExists,
  isFolderEmpty,
  read,
  areFoldersOverlapping,
  cleanFolder,
  deleteFolder,
  ReadMode,
};
